#ifndef MATRIX_H_
#define MATRIX_H_

#include "Vector.h"
#include <iomanip>

class Matrix {
private:
	int bar, kol;
	double **values;
public:
	Matrix() {
		values = NULL;
		bar = kol = 0;
	}
	Matrix(int b, int k) {
		bar = b;
		kol = k;
		values = new double*[b + 1];
		for (int i = 0; i <= b; i++) {
			values[i] = new double[k + 1];
		}
	}
	Matrix(const Matrix &m) {
		this->bar = m.bar;
		this->kol = m.kol;
		this->values = m.values;
	}
	Matrix & operator=(const Matrix &m) {
		this->bar = m.bar;
		this->kol = m.kol;
		this->values = m.values;
		for (int i = 1; i <= this->bar; i++) {
			for (int j = 1; j <= this->kol; j++) {
				values[i][j] = m.values[i][j];
			}
		}
		return *this;
	}
	double & operator()(int i, int k) {
		return values[i][k];
	}
	Matrix & operator+=(const double &d) {
		for (int i = 1; i <= bar; i++) {
			for (int j = 1; j <= kol; j++) {
				values[i][j] += d;
			}
		}
		return *this;
	}
	Matrix & operator-=(const double &d) {
		for (int i = 1; i <= bar; i++) {
			for (int j = 1; j <= kol; j++) {
				values[i][j] -= d;
			}
		}
		return *this;
	}
	Matrix & operator*=(const double &d) {
		for (int i = 1; i <= bar; i++) {
			for (int j = 1; j <= kol; j++) {
				values[i][j] *= d;
			}
		}
		return *this;
	}
	Matrix & operator/=(const double &d) {
		for (int i = 1; i <= bar; i++) {
			for (int j = 1; j <= kol; j++) {
				values[i][j] /= d;
			}
		}
		return *this;
	}

	Matrix operator+(const Matrix &m) {
		Matrix x = Matrix(m.bar, m.kol);
		double **v = x.values;
		for (int i = 1; i <= x.bar; i++) {
			for (int j = 1; j <= x.kol; j++) {
				v[i][j] = values[i][j] + m.values[i][j];
			}
		}
		return x;
	}
	Matrix operator-(const Matrix &m) {
		Matrix x = Matrix(m.bar, m.kol);
		double **v = x.values;
		for (int i = 1; i <= x.bar; i++) {
			for (int j = 1; j <= x.kol; j++) {
				v[i][j] = values[i][j] - m.values[i][j];
			}
		}
		return x;
	}

	Matrix operator*(const Matrix &m) {
		Matrix x = Matrix(m.bar, m.kol);
		double **v = x.values;
		for (int i = 1; i <= x.bar; i++) {
			for (int j = 1; j <= x.kol; j++) {
				v[i][j] = 0;
				for (int k = 1; k <= kol; k++)
					v[i][j] += values[i][k] * m.values[k][j];
			}
		}
		return x;
	}
	Matrix operator*(const double &d){
		Matrix x = Matrix(bar, kol);
		double **v = x.values;
		for (int i = 1; i <= x.bar; i++) {
			for (int j = 1; j <= x.kol; j++) {
				v[i][j] = d*values[i][j];
			}
		}
		return x;	
	}
	Vector operator*(Vector &m) {
		Vector v = Vector(bar);
		double *x = v.getValues();
		for (int i = 1; i <= v.Row(); i++) {
			for (int j = 1; j <= m.Row(); j++) {
				x[i] = 0;
				for (int k = 1; k <= kol; k++)
					x[i] += values[i][k] * m(k);
			}
		}
		return v;
	}

	static Matrix zeros(int bar, int col) {
		Matrix z = Matrix(bar, col);
		for (int i = 1; i <= z.bar; i++) {
			for (int j = 1; j <= z.kol; j++) {
				z.values[i][j] = 0.0;
			}
		}
		return z;
	}

	static Matrix iso(const int &bar, const int &col, const double &v) {
		Matrix z = Matrix(bar, col);
		for (int i = 1; i <= z.bar; i++) {
			for (int j = 1; j <= z.kol; j++) {
				z.values[i][j] = v;
			}
		}
		return z;
	}

	static Matrix diag(int bar, int col) {
		Matrix z = Matrix(bar, col);
		for (int i = 1; i <= z.bar; i++) {
			for (int j = 1; j <= z.kol; j++) {
				if (i == j)
					z.values[i][j] = 1.0;
				else
					z.values[i][j] = 0.0;
			}
		}
		return z;
	}

	static Matrix diag(int bar, int col, double diagv, double nd) {
		Matrix z = Matrix(bar, col);
		for (int i = 1; i <= z.bar; i++) {
			for (int j = 1; j <= z.kol; j++) {
				if (i == j)
					z.values[i][j] = diagv;
				else
					z.values[i][j] = nd;
			}
		}
		return z;
	}

	static Matrix isoBar(const double &a, const double &b, const double &c) {
		Matrix m = Matrix(3, 3);
		m.values[1][1] = a;
		m.values[1][2] = a;
		m.values[1][3] = a;
		m.values[2][1] = b;
		m.values[2][2] = b;
		m.values[2][3] = b;
		m.values[3][1] = c;
		m.values[3][2] = c;
		m.values[3][3] = c;
		return m;
	}

	static Matrix isoColumn(const double &a, const double &b, const double &c) {
		Matrix m = Matrix(3, 3);
		m.values[1][1] = a;
		m.values[1][2] = b;
		m.values[1][3] = c;
		m.values[2][1] = a;
		m.values[2][2] = b;
		m.values[2][3] = c;
		m.values[3][1] = a;
		m.values[3][2] = b;
		m.values[3][3] = c;
		return m;
	}

	void set(int i, int j, const double &v) {
		values[i][j] = v;
	}
	int row()const{
		return bar;
	}
	int column()const{
		return kol;
	}
	double ** getValues(){
		return values;
	}
	virtual ~Matrix(){
	}
};

#endif /* MATRIX_H_ */
